using Config;
using Cysharp.Threading.Tasks;
using Data;
using MoonFramework.FSM;
using MoonFramework.Scripts.Event;
using MoonFramework.Scripts.Tool.System.Setting;
using MoonFramework.Scripts.View.Music;
using MoonFramework.Template;
using Tool;
using Tool.Name;
using UnityEngine;

namespace GameTime
{
    public class MorningTime : BaseTimeState
    {
        public override void Enter()
        {
            base.Enter();
            EventCenter.Instance.Callback(EventName.UpdateTimeStat, true);
            EventCenter.Instance.Callback(EventName.UpdateDay, _timeData.curDay);
            EventCenter.Instance.Callback(EventName.OnMorning);
            _timeData.curDay++;
            // 1/6 的概率刷出大雾天气
            if (Random.Range(0, 6) == 0)
            {
                RenderSettings.fog = true;
                Fog().Forget();
            }
        }

        private async UniTaskVoid Fog()
        {
            while (RenderSettings.fog)
            {
                RenderSettings.fogDensity = 0.1f - (1 - ratio);
                await UniTask.Yield();
            }
        }

        public override void Exit()
        {
            base.Exit();
            if(RenderSettings.fog)
                RenderSettings.fog = false;
        }
    }
    
    public class NoonTime : BaseTimeState
    {
    }
    
    public class NightfallTime : BaseTimeState
    {
        public override void Enter()
        {
            base.Enter();
            EventCenter.Instance.Callback(EventName.UpdateTimeStat, false);
        }
    }
    
    public class NightTime : BaseTimeState
    {
    }

    public abstract class BaseTimeState : IState
    {
        public TimeStateData TimeStateData;
        protected float ratio;
        private TimeFsm fsmMachine;
        protected TimeData _timeData;

        public void Init(TimeStateData timeStateData, TimeFsm timeFsm)
        {
            _timeData = ArchiveManager.Instance.TimeData;
            this.TimeStateData = timeStateData;
            fsmMachine = timeFsm;
        }

        public bool CheckAndCalTime(float curTime, string nextStateName, out Quaternion rotation, out Color color,
            out float sunIntensity)
        {
            var nextState = fsmMachine.fsmStates[nextStateName] as BaseTimeState;
            
            ratio = 1f - curTime / TimeStateData.durationTime;
            rotation = Quaternion.Slerp(TimeStateData.sunQuaternion, nextState.TimeStateData.sunQuaternion, ratio);
            color = Color.Lerp(TimeStateData.sunColor, nextState.TimeStateData.sunColor, ratio);
            sunIntensity = Mathf.Lerp(TimeStateData.sunIntensity, nextState.TimeStateData.sunIntensity, ratio);
            
            return curTime > 0;
        }

        public virtual void Enter()
        {
            if (TimeStateData.BgAudioClip) fsmMachine.ChangeBgAudio(TimeStateData.BgAudioClip).Forget();
        }

        public virtual void Exit()
        {
        }
    }

    /// <summary>
    /// 时间状态机
    /// </summary>
    public class TimeFsm : BaseFsm<TimeManager>
    {
        public TimeFsm(string curState, TimeManager time)
        {
            base.Init(curState, time);
            var timeConfig1 = ConfigManager.Instance.GetConfig<TimeConfig>(ConfigName.Time, 0);
            var states = new[]
            {
                AllocateState("MorningTime", () => new MorningTime()) as BaseTimeState,
                AllocateState("NoonTime", () => new NoonTime()) as BaseTimeState,
                AllocateState("NightfallTime", () => new NightfallTime()) as BaseTimeState,
                AllocateState("NightTime", () => new NightTime()) as BaseTimeState
            };
            for (var i = 0; i < states.Length; i++) states[i].Init(timeConfig1.timeDatas[i], this);
            AddTransition(nameof(MorningTime), nameof(NoonTime));
            AddTransition(nameof(NoonTime), nameof(NightfallTime));
            AddTransition(nameof(NightfallTime), nameof(NightTime));
            AddTransition(nameof(NightTime), nameof(MorningTime));
            fsmStates[curState].Enter();
        }

        public async UniTaskVoid ChangeBgAudio(AudioClip audioClip)
        {
            var old = AudioManager.Instance.BGVolume;
            if (old <= 0) return;
            var cur = old;
            
            while (cur > 0)
            {
                await UniTask.Yield();
                cur -= Time.deltaTime;
                AudioManager.Instance.BGVolume = cur;
            }

            AudioManager.Instance.PlayBgAudio(audioClip);
            
            while (cur < old)
            {
                await UniTask.Yield();
                cur += Time.deltaTime / 2;
                AudioManager.Instance.BGVolume = cur;
            }

            AudioManager.Instance.BGVolume = old;
        }
    }
}